/*-*-C-*-
 * ResEd shell Object Flags dialogue
 */

#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "dbox.h"
#include "interactor.h"
#include "registry.h"

#include "class.h"
#include "document.h"
#include "icondefs.h"
#include "objflags.h"


static WindowPtr window = NULL;
static Bool asmenu = FALSE;


/*
 * These reflect the order of the buttons[] array in the struct below
 */

#define HOW_UNK -1
#define HOW_YES 0
#define HOW_NO 1
#define HOW_ASIS 2


/* 
 * This table describes the dialogue box.  A little bit of data can save a lot of code
 */

static struct
{
    int bit;
    int buttons[3];
} flags[] =
{
    OBJECT_CREATEONLOAD, {I_OBJFLAGS_AUTOCREATE_YES, I_OBJFLAGS_AUTOCREATE_NO, I_OBJFLAGS_AUTOCREATE_ASIS},
    OBJECT_SHOWONCREATE, {I_OBJFLAGS_AUTOSHOW_YES,   I_OBJFLAGS_AUTOSHOW_NO,   I_OBJFLAGS_AUTOSHOW_ASIS},
    OBJECT_SHARED,       {I_OBJFLAGS_SHARED_YES,     I_OBJFLAGS_SHARED_NO,     I_OBJFLAGS_SHARED_ASIS},
    OBJECT_ANCESTOR,     {I_OBJFLAGS_ANCESTOR_YES,   I_OBJFLAGS_ANCESTOR_NO,   I_OBJFLAGS_ANCESTOR_ASIS},
    0, {0, 0, 0}
};


/*
 * Load templates, etc.
 */

error * objflags_load_prototypes ()
{
   ER ( wimp_load_template("ObjFlags", &window) );
   ER ( swi (Wimp_CreateWindow,  R1, &window->visarea,
             OUT,  R0, &window->handle,  END) );
   return registry_register_window (window->handle, ObjectFlags, NULL);
}


/*
 * Set up the initial button settings by looking at the selection.
 */

static error * fill_in (DocumentPtr doc)
{
    int i, j;
    if (doc->numselected == 0)
    {
        for (i = 0; flags[i].bit != 0; i++)
            for (j = HOW_YES; j <= HOW_ASIS; j++)
            {
                dbox_setbutton (window, flags[i].buttons[j], FALSE);
                dbox_shade (window, flags[i].buttons[j], TRUE);
            }
    }
    else
    {
        for (i = 0; flags[i].bit != 0; i++)
        {
            int how = HOW_UNK;
            ResourcePtr res = doc->resources;
            for (; res; res = res->next)
            {
                if (res->selected)
                {
                    if (res->object->hdr.flags & flags[i].bit)
                    {
                        if (how == HOW_UNK)
                            how = HOW_YES;
                        else if (how == HOW_NO)
                            how = HOW_ASIS;
                    }
                    else
                    {
                        if (how == HOW_UNK)
                            how = HOW_NO;
                        else if (how == HOW_YES)
                            how = HOW_ASIS;
                    }
                }
            }

            dbox_shade (window, flags[i].buttons[HOW_YES],  FALSE);
            dbox_shade (window, flags[i].buttons[HOW_NO],   FALSE);
            dbox_shade (window, flags[i].buttons[HOW_ASIS], how != HOW_ASIS);

            dbox_setbutton (window, flags[i].buttons[HOW_YES],  how == HOW_YES);
            dbox_setbutton (window, flags[i].buttons[HOW_NO],   how == HOW_NO);
            dbox_setbutton (window, flags[i].buttons[HOW_ASIS], how == HOW_ASIS);
        }
    }
    return NULL;
}


/*
 * Update the selected resources' flags
 */

static error * update (DocumentPtr doc)
{
    ResourcePtr res = doc->resources;
    Bool altered = FALSE;

    for (; res; res = res->next)
    {
        if (res->selected)
        {
            int i;
            unsigned int mask = 0, value = 0;
            unsigned int oldflags = res->object->hdr.flags;

            for (i = 0; flags[i].bit != 0; i++)
            {
                if (dbox_getbutton (window, flags[i].buttons[HOW_YES]))
                {
                    mask  |= flags[i].bit;
                    value |= flags[i].bit;
                }
                else if (dbox_getbutton (window, flags[i].buttons[HOW_NO]))
                    mask  |= flags[i].bit;
            }

            res->object->hdr.flags &= ~mask;
            res->object->hdr.flags |= value;

            if (oldflags != res->object->hdr.flags)
                altered = TRUE;
        }
    }

    if (altered)
        document_modified (doc, TRUE);

    return NULL;
}


/*
 * Interactor for the objflags window.  Expects to be pushed on top of the
 * menu.c interactor, so needs to be careful about popping itself correctly.
 */

static error * objflags_interactor (unsigned int event, int *buf, void *closure, Bool *consumed)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    WindowPtr win = (WindowPtr) buf;         /* only half there */
    KeyPressPtr key = (KeyPressPtr) buf;
    error *err = NULL;
    DocumentPtr doc = (DocumentPtr) closure;

    if (buf == NULL)                     /* we are being asked to cancel */
        return NULL;

    switch (event)
    {
    case EV_OPEN_WINDOW_REQUEST:
        if (win->handle == window->handle)
        {
            *consumed = TRUE;
            window->visarea = win->visarea;
            window->scrolloffset = win->scrolloffset;
            window->behind = win->behind;
            err = swi (Wimp_OpenWindow, R1, window, END);
        }
        break;

    case EV_KEY_PRESSED:
        if (key->caret.windowhandle == window->handle)
        {
            unsigned int modifiers = wimp_read_modifiers ();

            *consumed = TRUE;
            switch (key->code)
            {
            case 0x0d:   /* RETURN */
                err = update (doc);
                if (err)
                {
                    error_box (err);
                    break;
                }

            case 0x1b:   /* ESCAPE */
                if ((modifiers & MODIFIER_SHIFT) == 0)
                {
                    if (asmenu)
                        swi (Wimp_CreateMenu, R1, -1, END);
                    /* return input focus to parent document viewer window */
                    dbox_set_caret_to (&doc->window, -1);
                    interactor_cancel ();
                }
                else
                    fill_in (doc);
                break;

            default:
                *consumed = FALSE;
                break;
            }
        }
        break;

    case EV_MOUSE_CLICK:
        if (mouse->windowhandle == window->handle)
        {
            *consumed = TRUE;
            switch (mouse->iconhandle)
            {
            case I_OBJFLAGS_OK:
                if (mouse->buttons == MB_CLICK(MB_SELECT) ||
                    mouse->buttons == MB_CLICK(MB_ADJUST))
                {
                    err = update (doc);
                    if (err)
                    {
                        error_box (err);
                        break;
                    }
               }

            case I_OBJFLAGS_CANCEL:
                if (mouse->buttons == MB_CLICK(MB_SELECT))
                {
                    if (asmenu)
                        swi (Wimp_CreateMenu, R1, -1, END);
                    /* return input focus to parent document viewer window */
                    dbox_set_caret_to (&doc->window, -1);
                    interactor_cancel ();
                }
                else if (mouse->buttons == MB_CLICK(MB_ADJUST))
                    fill_in (doc);
                break;

            /* ensure that ADJ-click on a radio button doesn't turn it off */
            case I_OBJFLAGS_AUTOCREATE_YES:
            case I_OBJFLAGS_AUTOCREATE_NO:
            case I_OBJFLAGS_AUTOCREATE_ASIS:
            case I_OBJFLAGS_AUTOSHOW_YES:
            case I_OBJFLAGS_AUTOSHOW_NO:
            case I_OBJFLAGS_AUTOSHOW_ASIS:
            case I_OBJFLAGS_SHARED_YES:
            case I_OBJFLAGS_SHARED_NO:
            case I_OBJFLAGS_SHARED_ASIS:
            case I_OBJFLAGS_ANCESTOR_YES:
            case I_OBJFLAGS_ANCESTOR_NO:
            case I_OBJFLAGS_ANCESTOR_ASIS:
                if ( mouse->buttons == MB_CLICK(MB_ADJUST) &&
                     !dbox_getbutton (window, mouse->iconhandle))
                    dbox_setbutton (window, mouse->iconhandle, TRUE);
                break;
            }
        }
        break;
    }

    return err;
}


/*
 * Open the objflags dialogue as a submenu at the appointed position.
 *
 * If position is NULL, then the dbox is to be opened as a transient dbox in its
 *  default position.
 *
 * If position is not NULL, then the dbox is to be opened as a submenu at the
 *  specified position.
 */

error * objflags_open (DocumentPtr doc, PointPtr position)
{
    ER ( fill_in (doc) );

    if (position == NULL)
    {
        asmenu = TRUE;

        ER ( swi (Wimp_CreateMenu, R1, window->handle,
                                   R2, window->visarea.minx,
                                   R3, window->visarea.maxy, END) );
        interactor_install (objflags_interactor, (void *) doc);

        /* give input focus to the dbox */
        ER ( dbox_set_caret_to (window, -1) );
    }
    else
    {
        asmenu = FALSE;

        /* record new position of window in case opened by key next time */
        window->visarea.maxx = position->x +
                               (window->visarea.maxx - window->visarea.minx);
        window->visarea.minx = position->x;
        window->visarea.miny = position->y -
                               (window->visarea.maxy - window->visarea.miny);
        window->visarea.maxy = position->y;

        ER ( swi (Wimp_CreateSubMenu,  R1, window->handle,
                                       R2, position->x,
                                       R3, position->y,  END) );

        /* give input focus to the dbox */
        ER ( dbox_set_caret_to (window, -1) );
        interactor_push (objflags_interactor, (void *) doc);
    }

    return NULL;
}


